iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Modern Web

給前端新手的圖文故事系列 第 24

了解 React 中的資料流與相關 Hook 操作

  • 分享至 

  • xImage
  •  

React 中的資料流

在 React 中的資料流是一個非常值得探討的主題,首先明確一點,React 是所謂的單向資料流,因此大多數的時間裡,組件都會藉由傳入參數的方法(Props)來注入呼叫組件的變數,而自己組件內的我們通常都稱之為狀態(Stats),在通常的情況下,都是會從父組件傳入到子組件,已讓子組件可以使用父組件的資料。

最常見的 Props

傳入(props)最常見的地方,其實是在做一個類似組件的共用,如按鈕組件更換顏色,我們就可以單純使用 Props 來進行調度

組件設計

const Btn = (props) => {
  const { color, onClick } = props;
  return (
    <button style={{ backgroundColor: color,padding:'20px',color:'white' }} onClick={onClick}>
      Click me {color}
    </button>
  );
};

組件使用

<Btn color="red" onClick={() => console.log('red')} />
<Btn color="blue" onClick={() => console.log('blue')} />

https://ithelp.ithome.com.tw/upload/images/20231012/20162093vB6r7k2YMR.png

從 Todo list 中暸解狀態(state)與傳入(props)

以下是以個簡單的範例程式,我們在父組件上做了一個 useState 作為資料儲存,並且建立了一個刪除項目的函式,作為我們後續做刪除的調度使用。

在這個範例中,TodoItem 組件的任務是藉由 Props 傳入的內容,進行後續的顯示與操作,但這個組件本身會更新的的時機點,其實還是由父層傳入的 todo 是否變更來決定,在資料流上他其實是因為父層的 todo 這個 state 變動,在傳入時他也確認有異動,因此才會觸發畫面的更新。
附帶一提,直接傳入 setTodoList Function 來使用也可以觸發異動,因為他都是在父層處理的資料流,而在設計上我個人不建議這樣傳入,因為相同類型的邏輯最好寫在父層會比較好,不過這個就看每個人喜歡的設計方式了

https://ithelp.ithome.com.tw/upload/images/20231012/20162093SxPSXcNRBn.png

import React, { useState } from 'react';

const TodoItem = (props) => {
  const { todo, removeTodo } = props;
  return (
    <article>
      <h2>{todo.title}</h2>
      <p>{todo.description}</p>
      <button onClick={() => removeTodo(todo._id)}>Remove</button>
    </article>
  );
};

const TodoList = () => {
  const [todoList, setTodoList] = useState([
    {
      _id: 1,
      title: 'Todo 1',
      description: 'Todo 1 description',
      isCompleted: false,
    },
    {
      _id: 1,
      title: 'Todo 1',
      description: 'Todo 1 description',
      isCompleted: false,
    },
  ]);

  const removeTodo = (todoId) => {
    const newTodoList = todoList.filter((todo) => todo._id !== todoId);
    setTodoList(newTodoList);
  };

  return (
    <article>
      <h1>TodoList</h1>
      <ul>
        {todoList.map((todo) => (
          <li key={todo._id}>
            <TodoItem todo={todo} removeTodo={removeTodo} />
          </li>
        ))}
      </ul>
    </article>
  );
};

export default TodoList;

附帶一提,其實更新畫面的行為在邏輯上也是副作用,因為我們的主要行為其實是刪除一個項目,而連動運作了 UI 的更新,則不是我們原先預計的主要目的。

順帶一提,組件在設計上也是可以傳入內文的

組件設計

const Box = (props) => {
  const { children } = props;
  return (
    <article>
      以下是使用它所包含的元素
      {children}
    </article>
  );
};


組件使用

<Box>我是內容喔</Box>

https://ithelp.ithome.com.tw/upload/images/20231012/20162093X2NMJq2Oyr.png

Props 的極限是什麼?

自 React 誕生以來,就有一個有趣的問題產生,那就是我們到底應該怎麼去管理與儲存資料流,在上面 todo list 的範例中,我們會在一個功能內完成資料流的處理,因此並不會有太大的問題,但是如果是像有會員系統的網站,那會員資訊可能就沒有那麼合適儲存在某個 Component 中了,他可能會用在以下的地方。

  • 會員資訊頁面
  • 會員彈窗
  • 聊天室
  • 活動申請
  • 身份認證
  • ....

而若將它放置在 APP 那一層(最父層),也會導致可能要傳入過多組件(Component),並且不易維護的問題,這一點可以在下面的範例中看到
https://ithelp.ithome.com.tw/upload/images/20231012/20162093iRJUsDTXuu.png
所以在實務的設計上,我們會盡可能規避掉上面這種令人恐懼的資料流,但是實際需求上確實會有這樣的情況發生,也就是一些狀態與屬性需要可以跨組件進行使用,所以在這些得基礎上,我們會開始使用一些其他的資料流處理方式。

使用 useContext 來輔助我們處理資料流

當跨組件的需求來臨時,我們可能會選擇像是 Redux 這類處理資料流的函式庫來處理,但在 React hook 中,其實也提供了一套非常方便的機制,而這就是 useContext。

在 useContext 的使用上,我們需要用 createContext 建立一個實體的 Context 來存儲資料,並且這個資料我們可以在起他的組件中,使用 useContext 來調度到他,以下是個簡單的範例。

import React, { createContext, useContext } from 'react';

const MyContext = createContext();

function ParentComponent() {
  return (
    <MyContext.Provider value="Hello from Context!">
      <ChildComponent />
    </MyContext.Provider>
  );
}

function ChildComponent() {
  const data = useContext(MyContext);
  return <p>{data}</p>;
}

在上面的範例中,我們的 MyContext.Provider 包覆了 ChildComponent,所以 ChildComponent 可以取得到 MyContext value 中所帶有的資料。


上一篇
學習基礎 Hook 使用與副作用(Side Effect)
下一篇
認知 React 的 Higher-Order Components
系列文
給前端新手的圖文故事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言